home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / misc / ispell-3.001 / ispell-3~ / ispell-3.1 / correct.c < prev    next >
C/C++ Source or Header  |  1995-10-12  |  44KB  |  1,711 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: correct.c,v 1.59 1995/08/05 23:19:43 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * correct.c - Routines to manage the higher-level aspects of spell-checking
  8.  *
  9.  * This code originally resided in ispell.c, but was moved here to keep
  10.  * file sizes smaller.
  11.  *
  12.  * Copyright (c), 1983, by Pace Willisson
  13.  *
  14.  * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
  15.  * All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or without
  18.  * modification, are permitted provided that the following conditions
  19.  * are met:
  20.  *
  21.  * 1. Redistributions of source code must retain the above copyright
  22.  *    notice, this list of conditions and the following disclaimer.
  23.  * 2. Redistributions in binary form must reproduce the above copyright
  24.  *    notice, this list of conditions and the following disclaimer in the
  25.  *    documentation and/or other materials provided with the distribution.
  26.  * 3. All modifications to the source code must be clearly marked as
  27.  *    such.  Binary redistributions based on modified source code
  28.  *    must be clearly marked as modified versions in the documentation
  29.  *    and/or other materials provided with the distribution.
  30.  * 4. All advertising materials mentioning features or use of this software
  31.  *    must display the following acknowledgment:
  32.  *      This product includes software developed by Geoff Kuenning and
  33.  *      other unpaid contributors.
  34.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  35.  *    products derived from this software without specific prior
  36.  *    written permission.
  37.  *
  38.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  39.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  40.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  41.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  42.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  43.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  44.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  45.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  46.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  47.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48.  * SUCH DAMAGE.
  49.  */
  50.  
  51. /*
  52.  * $Log: correct.c,v $
  53.  * Revision 1.59  1995/08/05  23:19:43  geoff
  54.  * Fix a bug that caused offsets for long lines to be confused if the
  55.  * line started with a quoting uparrow.
  56.  *
  57.  * Revision 1.58  1994/11/02  06:56:00  geoff
  58.  * Remove the anyword feature, which I've decided is a bad idea.
  59.  *
  60.  * Revision 1.57  1994/10/26  05:12:39  geoff
  61.  * Try boundary characters when inserting or substituting letters, except
  62.  * (naturally) at word boundaries.
  63.  *
  64.  * Revision 1.56  1994/10/25  05:46:30  geoff
  65.  * Fix an assignment inside a conditional that could generate spurious
  66.  * warnings (as well as being bad style).  Add support for the FF_ANYWORD
  67.  * option.
  68.  *
  69.  * Revision 1.55  1994/09/16  04:48:24  geoff
  70.  * Don't pass newlines from the input to various other routines, and
  71.  * don't assume that those routines leave the input unchanged.
  72.  *
  73.  * Revision 1.54  1994/09/01  06:06:41  geoff
  74.  * Change erasechar/killchar to uerasechar/ukillchar to avoid
  75.  * shared-library problems on HP systems.
  76.  *
  77.  * Revision 1.53  1994/08/31  05:58:38  geoff
  78.  * Add code to handle extremely long lines in -a mode without splitting
  79.  * words or reporting incorrect offsets.
  80.  *
  81.  * Revision 1.52  1994/05/25  04:29:24  geoff
  82.  * Fix a bug that caused line widths to be calculated incorrectly when
  83.  * displaying lines containing tabs.  Fix a couple of places where
  84.  * characters were sign-extended incorrectly, which could cause 8-bit
  85.  * characters to be displayed wrong.
  86.  *
  87.  * Revision 1.51  1994/05/17  06:44:05  geoff
  88.  * Add support for controlled compound formation and the COMPOUNDONLY
  89.  * option to affix flags.
  90.  *
  91.  * Revision 1.50  1994/04/27  05:20:14  geoff
  92.  * Allow compound words to be formed from more than two components
  93.  *
  94.  * Revision 1.49  1994/04/27  01:50:31  geoff
  95.  * Add support to correctly capitalize words generated as a result of a
  96.  * missing-space suggestion.
  97.  *
  98.  * Revision 1.48  1994/04/03  23:23:02  geoff
  99.  * Clean up the code in missingspace() to be a bit simpler and more
  100.  * efficient.
  101.  *
  102.  * Revision 1.47  1994/03/15  06:24:23  geoff
  103.  * Fix the +/-/~ commands to be independent.  Allow the + command to
  104.  * receive a suffix which is a deformatter type (currently hardwired to
  105.  * be either tex or nroff/troff).
  106.  *
  107.  * Revision 1.46  1994/02/21  00:20:03  geoff
  108.  * Fix some bugs that could cause bad displays in the interaction between
  109.  * TeX parsing and string characters.  Show_char now will not overrun
  110.  * the inverse-video display area by accident.
  111.  *
  112.  * Revision 1.45  1994/02/14  00:34:51  geoff
  113.  * Fix correct to accept length parameters for ctok and itok, so that it
  114.  * can pass them to the to/from ichar routines.
  115.  *
  116.  * Revision 1.44  1994/01/25  07:11:22  geoff
  117.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  118.  *
  119.  */
  120.  
  121. #include <ctype.h>
  122. #include "config.h"
  123. #include "ispell.h"
  124. #include "proto.h"
  125. #include "msgs.h"
  126. #include "version.h"
  127.  
  128. void        givehelp P ((int interactive));
  129. void        checkfile P ((void));
  130. void        correct P ((char * ctok, int ctokl, ichar_t * itok, int itokl,
  131.           char ** curchar));
  132. static void    show_line P ((char * line, char * invstart, int invlen));
  133. static int    show_char P ((char ** cp, int linew, int output, int maxw));
  134. static int    line_size P ((char * buf, char * bufend));
  135. static void    inserttoken P ((char * buf, char * start, char * tok,
  136.           char ** curchar));
  137. static int    posscmp P ((char * a, char * b));
  138. int        casecmp P ((char * a, char * b, int canonical));
  139. void        makepossibilities P ((ichar_t * word));
  140. static int    insert P ((ichar_t * word));
  141. #ifndef NO_CAPITALIZATION_SUPPORT
  142. static void    wrongcapital P ((ichar_t * word));
  143. #endif /* NO_CAPITALIZATION_SUPPORT */
  144. static void    wrongletter P ((ichar_t * word));
  145. static void    extraletter P ((ichar_t * word));
  146. static void    missingletter P ((ichar_t * word));
  147. static void    missingspace P ((ichar_t * word));
  148. int        compoundgood P ((ichar_t * word, int pfxopts));
  149. static void    transposedletter P ((ichar_t * word));
  150. static void    tryveryhard P ((ichar_t * word));
  151. static int    ins_cap P ((ichar_t * word, ichar_t * pattern));
  152. static int    save_cap P ((ichar_t * word, ichar_t * pattern,
  153.           ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]));
  154. int        ins_root_cap P ((ichar_t * word, ichar_t * pattern,
  155.           int prestrip, int preadd, int sufstrip, int sufadd,
  156.           struct dent * firstdent, struct flagent * pfxent,
  157.           struct flagent * sufent));
  158. static void    save_root_cap P ((ichar_t * word, ichar_t * pattern,
  159.           int prestrip, int preadd, int sufstrip, int sufadd,
  160.           struct dent * firstdent, struct flagent * pfxent,
  161.           struct flagent * sufent,
  162.           ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN],
  163.           int * nsaved));
  164. static char *    getline P ((char * buf));
  165. void        askmode P ((void));
  166. void        copyout P ((char ** cc, int cnt));
  167. static void    lookharder P ((char * string));
  168. #ifdef REGEX_LOOKUP
  169. static void    regex_dict_lookup P ((char * cmd, char * grepstr));
  170. #endif /* REGEX_LOOKUP */
  171.  
  172. void givehelp (interactive)
  173.     int            interactive;    /* NZ for interactive-mode help */
  174.     {
  175. #ifdef COMMANDFORSPACE
  176.     char ch;
  177. #endif
  178.     register FILE *helpout;    /* File to write help to */
  179.  
  180.     if (interactive)
  181.     {
  182.     erase ();
  183.     helpout = stdout;
  184.     }
  185.     else
  186.     helpout = stderr;
  187.  
  188.     (void) fprintf (helpout, CORR_C_HELP_1);
  189.     (void) fprintf (helpout, CORR_C_HELP_2);
  190.     (void) fprintf (helpout, CORR_C_HELP_3);
  191.     (void) fprintf (helpout, CORR_C_HELP_4);
  192.     (void) fprintf (helpout, CORR_C_HELP_5);
  193.     (void) fprintf (helpout, CORR_C_HELP_6);
  194.     (void) fprintf (helpout, CORR_C_HELP_7);
  195.     (void) fprintf (helpout, CORR_C_HELP_8);
  196.     (void) fprintf (helpout, CORR_C_HELP_9);
  197.  
  198.     (void) fprintf (helpout, CORR_C_HELP_COMMANDS);
  199.  
  200.     (void) fprintf (helpout, CORR_C_HELP_R_CMD);
  201.     (void) fprintf (helpout, CORR_C_HELP_BLANK);
  202.     (void) fprintf (helpout, CORR_C_HELP_A_CMD);
  203.     (void) fprintf (helpout, CORR_C_HELP_I_CMD);
  204.     (void) fprintf (helpout, CORR_C_HELP_U_CMD);
  205.     (void) fprintf (helpout, CORR_C_HELP_0_CMD);
  206.     (void) fprintf (helpout, CORR_C_HELP_L_CMD);
  207.     (void) fprintf (helpout, CORR_C_HELP_X_CMD);
  208.     (void) fprintf (helpout, CORR_C_HELP_Q_CMD);
  209.     (void) fprintf (helpout, CORR_C_HELP_BANG);
  210.     (void) fprintf (helpout, CORR_C_HELP_REDRAW);
  211.     (void) fprintf (helpout, CORR_C_HELP_SUSPEND);
  212.     (void) fprintf (helpout, CORR_C_HELP_HELP);
  213.  
  214.     if (interactive)
  215.     {
  216.     (void) fprintf (helpout, "\r\n\r\n");
  217.     (void) fprintf (helpout, CORR_C_HELP_TYPE_SPACE);
  218.     (void) fflush (helpout);
  219. #ifdef COMMANDFORSPACE
  220.     ch = GETKEYSTROKE ();
  221.     if (ch != ' ' && ch != '\n' && ch != '\r')
  222.         (void) ungetc (ch, stdin);
  223. #else
  224.     while (GETKEYSTROKE () != ' ')
  225.         ;
  226. #endif
  227.     }
  228.     }
  229.  
  230. void checkfile ()
  231.     {
  232.     int        bufno;
  233.     int        bufsize;
  234.     int        ch;
  235.  
  236.     for (bufno = 0;  bufno < contextsize;  bufno++)
  237.     contextbufs[bufno][0] = '\0';
  238.  
  239.     for (  ;  ;  )
  240.     {
  241.     for (bufno = contextsize;  --bufno > 0;  )
  242.         (void) strcpy (contextbufs[bufno],
  243.           contextbufs[bufno - 1]);
  244.     if (quit)    /* quit can't be set in l mode */
  245.         {
  246.         while (fgets (contextbufs[0],
  247.           sizeof contextbufs[0], infile) != NULL)
  248.         (void) fputs (contextbufs[0], outfile);
  249.         break;
  250.         }
  251.     /*
  252.      * Only read in enough characters to fill half this buffer so that any
  253.      * corrections we make are not likely to cause an overflow.
  254.      */
  255.     if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, infile)
  256.       == NULL)
  257.         break;
  258.     /*
  259.      * If we didn't read to end-of-line, we may have ended the
  260.      * buffer in the middle of a word.  So keep reading until we
  261.      * see some sort of character that can't possibly be part of a
  262.      * word. (or until the buffer is full, which fortunately isn't
  263.      * all that likely).
  264.      */
  265.     bufsize = strlen (contextbufs[0]);
  266.     if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
  267.         {
  268.         ch = (unsigned char) contextbufs[0][bufsize - 1];
  269.         while (bufsize < sizeof contextbufs[0] - 1
  270.           &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
  271.           ||  isstringstart (ch)))
  272.         {
  273.         ch = getc (infile);
  274.         if (ch == EOF)
  275.             break;
  276.         contextbufs[0][bufsize++] = (char) ch;
  277.         contextbufs[0][bufsize] = '\0';
  278.         }
  279.         }
  280.     checkline (outfile);
  281.     }
  282.     }
  283.  
  284. void correct (ctok, ctokl, itok, itokl, curchar)
  285.     char *        ctok;
  286.     int            ctokl;
  287.     ichar_t *        itok;
  288.     int            itokl;
  289.     char **        curchar;
  290.     {
  291.     register int    c;
  292.     register int    i;
  293.     int            col_ht;
  294.     int            ncols;
  295.     char *        start_l2;
  296.     char *        begintoken;
  297.  
  298.     begintoken = *curchar - strlen (ctok);
  299.  
  300.     if (icharlen (itok) <= minword)
  301.     return;            /* Accept very short words */
  302.  
  303. checkagain:
  304.     if (good (itok, 0, 0, 0, 0)  ||  compoundgood (itok, 0))
  305.     return;
  306.  
  307.     erase ();
  308.     (void) printf ("    %s", ctok);
  309.     if (currentfile)
  310.     (void) printf (CORR_C_FILE_LABEL, currentfile);
  311.     if (readonly)
  312.     (void) printf (" %s", CORR_C_READONLY);
  313.     (void) printf ("\r\n\r\n");
  314.  
  315.     makepossibilities (itok);
  316.  
  317.     /*
  318.      * Make sure we have enough room on the screen to hold the
  319.      * possibilities.  Reduce the list if necessary.  co / (maxposslen + 8)
  320.      * is the maximum number of columns that will fit.  col_ht is the
  321.      * height of the columns.  The constant 4 allows 2 lines (1 blank) at
  322.      * the top of the screen, plus another blank line between the
  323.      * columns and the context, plus a final blank line at the bottom
  324.      * of the screen for command entry (R, L, etc).
  325.      */
  326.     col_ht = li - contextsize - 4 - minimenusize;
  327.     ncols = co / (maxposslen + 8);
  328.     if (pcount > ncols * col_ht)
  329.     pcount = ncols * col_ht;
  330.  
  331. #ifdef EQUAL_COLUMNS
  332.     /*
  333.      * Equalize the column sizes.  The last column will be short.
  334.      */
  335.     col_ht = (pcount + ncols - 1) / ncols;
  336. #endif
  337.  
  338.     for (i = 0; i < pcount; i++)
  339.     {
  340. #ifdef BOTTOMCONTEXT
  341.     move (2 + (i % col_ht), (maxposslen + 8) * (i / col_ht));
  342. #else /* BOTTOMCONTEXT */
  343.     move (3 + contextsize + (i % col_ht), (maxposslen + 8) * (i / col_ht));
  344. #endif /* BOTTOMCONTEXT */
  345.     if (i >= easypossibilities)
  346.         (void) printf ("??: %s", possibilities[i]);
  347.     else if (easypossibilities >= 10  &&  i < 10)
  348.         (void) printf ("0%d: %s", i, possibilities[i]);
  349.     else
  350.         (void) printf ("%2d: %s", i, possibilities[i]);
  351.     }
  352.  
  353. #ifdef BOTTOMCONTEXT
  354.     move (li - contextsize - 1 - minimenusize, 0);
  355. #else /* BOTTOMCONTEXT */
  356.     move (2, 0);
  357. #endif /* BOTTOMCONTEXT */
  358.     for (i = contextsize;  --i > 0;  )
  359.     show_line (contextbufs[i], contextbufs[i], 0);
  360.  
  361.     start_l2 = contextbufs[0];
  362.     if (line_size (contextbufs[0], *curchar) > co - (sg << 1) - 1)
  363.     {
  364.     start_l2 = begintoken - (co / 2);
  365.     while (start_l2 < begintoken)
  366.         {
  367.         i = line_size (start_l2, *curchar) + 1;
  368.         if (i + (sg << 1) <= co)
  369.         break;
  370.         start_l2 += i - co;
  371.         }
  372.     if (start_l2 > begintoken)
  373.         start_l2 = begintoken;
  374.     if (start_l2 < contextbufs[0])
  375.         start_l2 = contextbufs[0];
  376.     }
  377.     show_line (start_l2, begintoken, (int) strlen (ctok));
  378.  
  379.     if (minimenusize != 0)
  380.     {
  381.     move (li - 2, 0);
  382.     (void) printf (CORR_C_MINI_MENU);
  383.     }
  384.  
  385.     for (  ;  ;  )
  386.     {
  387.     (void) fflush (stdout);
  388.     switch (c = (GETKEYSTROKE () & NOPARITY))
  389.         {
  390.         case 'Z' & 037:
  391.         stop ();
  392.         erase ();
  393.         goto checkagain;
  394.         case ' ':
  395.         erase ();
  396.         (void) fflush (stdout);
  397.         return;
  398.         case 'q': case 'Q':
  399.         if (changes)
  400.             {
  401.             (void) printf (CORR_C_CONFIRM_QUIT);
  402.             (void) fflush (stdout);
  403.             c = (GETKEYSTROKE () & NOPARITY);
  404.             }
  405.         else
  406.             c = 'y';
  407.         if (c == 'y' || c == 'Y')
  408.             {
  409.             erase ();
  410.             (void) fflush (stdout);
  411.             done (0);
  412.             }
  413.         goto checkagain;
  414.         case 'i': case 'I':
  415.         treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
  416.          ICHARTOSSTR_SIZE, 1);
  417.         erase ();
  418.         (void) fflush (stdout);
  419.         changes = 1;
  420.         return;
  421.         case 'u': case 'U':
  422.         itok = strtosichar (ctok, 0);
  423.         lowcase (itok);
  424.         treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
  425.         erase ();
  426.         (void) fflush (stdout);
  427.         changes = 1;
  428.         return;
  429.         case 'a': case 'A':
  430.         treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
  431.           ICHARTOSSTR_SIZE, 0);
  432.         erase ();
  433.         (void) fflush (stdout);
  434.         return;
  435.         case 'L' & 037:
  436.         goto checkagain;
  437.         case '?':
  438.         givehelp (1);
  439.         goto checkagain;
  440.         case '!':
  441.         {
  442.         char    buf[200];
  443.  
  444.         move (li - 1, 0);
  445.         (void) putchar ('!');
  446.         if (getline (buf) == NULL)
  447.             {
  448.             (void) putchar (7);
  449.             erase ();
  450.             (void) fflush (stdout);
  451.             goto checkagain;
  452.             }
  453.         (void) printf ("\r\n");
  454.         (void) fflush (stdout);
  455. #ifdef    USESH
  456.         shescape (buf);
  457. #else
  458.         (void) shellescape (buf);
  459. #endif
  460.         erase ();
  461.         goto checkagain;
  462.         }
  463.         case 'r': case 'R':
  464.         move (li - 1, 0);
  465.         if (readonly)
  466.             {
  467.             (void) putchar (7);
  468.             (void) printf ("%s ", CORR_C_READONLY);
  469.             }
  470.         (void) printf (CORR_C_REPLACE_WITH);
  471.         if (getline (ctok) == NULL)
  472.             {
  473.             (void) putchar (7);
  474.             /* Put it back */
  475.             (void) ichartostr (ctok, itok, ctokl, 0);
  476.             }
  477.         else
  478.             {
  479.             inserttoken (contextbufs[0],
  480.               begintoken, ctok, curchar);
  481.             if (strtoichar (itok, ctok, itokl, 0))
  482.             {
  483.             (void) putchar (7);
  484.             (void) printf (WORD_TOO_LONG (ctok));
  485.             }
  486.             changes = 1;
  487.             }
  488.         erase ();
  489.         if (icharlen (itok) <= minword)
  490.             return;        /* Accept very short replacements */
  491.         goto checkagain;
  492.         case '0': case '1': case '2': case '3': case '4':
  493.         case '5': case '6': case '7': case '8': case '9':
  494.         i = c - '0';
  495.         if (easypossibilities >= 10)
  496.             {
  497.             c = GETKEYSTROKE () & NOPARITY;
  498.             if (c >= '0'  &&  c <= '9')
  499.             i = i * 10 + c - '0';
  500.             else if (c != '\r'  &&  c != '\n')
  501.             {
  502.             (void) putchar (7);
  503.             break;
  504.             }
  505.             }
  506.         if (i < easypossibilities)
  507.             {
  508.             (void) strcpy (ctok, possibilities[i]);
  509.             changes = 1;
  510.             inserttoken (contextbufs[0],
  511.             begintoken, ctok, curchar);
  512.             erase ();
  513.             if (readonly)
  514.             {
  515.             move (li - 1, 0);
  516.             (void) putchar (7);
  517.             (void) printf ("%s", CORR_C_READONLY);
  518.             (void) fflush (stdout);
  519.             (void) sleep ((unsigned) 2);
  520.             }
  521.             return;
  522.             }
  523.         (void) putchar (7);
  524.         break;
  525.         case '\r':    /* This makes typing \n after single digits */
  526.         case '\n':    /* ..less obnoxious */
  527.         break;
  528.         case 'l': case 'L':
  529.         {
  530.         char    buf[100];
  531.         move (li - 1, 0);
  532.         (void) printf (CORR_C_LOOKUP_PROMPT);
  533.         if (getline (buf) == NULL)
  534.             {
  535.             (void) putchar (7);
  536.             erase ();
  537.             goto checkagain;
  538.             }
  539.         (void) printf ("\r\n");
  540.         (void) fflush (stdout);
  541.         lookharder (buf);
  542.         erase ();
  543.         goto checkagain;
  544.         }
  545.         case 'x': case 'X':
  546.         quit = 1;
  547.         erase ();
  548.         (void) fflush (stdout);
  549.         return;
  550.         default:
  551.         (void) putchar (7);
  552.         break;
  553.         }
  554.     }
  555.     }
  556.  
  557. static void show_line (line, invstart, invlen)
  558.     char *        line;
  559.     register char *    invstart;
  560.     register int    invlen;
  561.     {
  562.     register int    width;
  563.  
  564.     width = invlen ? (sg << 1) : 0;
  565.     while (line < invstart  &&  width < co - 1)
  566.     width += show_char (&line, width, 1, invstart - line);
  567.     if (invlen)
  568.     {
  569.     inverse ();
  570.     invstart += invlen;
  571.     while (line < invstart  &&  width < co - 1)
  572.         width += show_char (&line, width, 1, invstart - line);
  573.     normal ();
  574.     }
  575.     while (*line  &&  width < co - 1)
  576.     width += show_char (&line, width, 1, 0);
  577.     (void) printf ("\r\n");
  578.     }
  579.  
  580. static int show_char (cp, linew, output, maxw)
  581.     register char **    cp;
  582.     int            linew;
  583.     int            output;        /* NZ to actually do output */
  584.     int            maxw;        /* NZ to limit width shown */
  585.     {
  586.     register int    ch;
  587.     register int    i;
  588.     int            len;
  589.     ichar_t        ichar;
  590.     register int    width;
  591.  
  592.     ch = (unsigned char) **cp;
  593.     if (l1_isstringch (*cp, len, 0))
  594.     ichar = SET_SIZE + laststringch;
  595.     else
  596.     ichar = chartoichar (ch);
  597.     if (!vflag  &&  iswordch (ichar)  &&  len == 1)
  598.     {
  599.     if (output)
  600.         (void) putchar (ch);
  601.     (*cp)++;
  602.     return 1;
  603.     }
  604.     if (ch == '\t')
  605.     {
  606.     if (output)
  607.         (void) putchar ('\t');
  608.     (*cp)++;
  609.     return 8 - (linew & 0x07);
  610.     }
  611.     /*
  612.      * Character is non-printing, or it's ISO and vflag is set.  Display
  613.      * it in "cat -v" form.  For string characters, display every element
  614.      * separately in that form.
  615.      */
  616.     width = 0;
  617.     if (maxw != 0  &&  len > maxw)
  618.     len = maxw;            /* Don't show too much */
  619.     for (i = 0;  i < len;  i++)
  620.     {
  621.     ch = (unsigned char) *(*cp)++;
  622.     if (ch > '\177')
  623.         {
  624.         if (output)
  625.         {
  626.         (void) putchar ('M');
  627.         (void) putchar ('-');
  628.         }
  629.         width += 2;
  630.         ch &= 0x7f;
  631.         }
  632.     if (ch < ' '  ||  ch == '\177')
  633.         {
  634.         if (output)
  635.         {
  636.         (void) putchar ('^');
  637.         if (ch == '\177')
  638.             (void) putchar ('?');
  639.         else
  640.             (void) putchar (ch + 'A' - '\001');
  641.         }
  642.         width += 2;
  643.         }
  644.     else
  645.         {
  646.         if (output)
  647.         (void) putchar (ch);
  648.         width += 1;
  649.         }
  650.     }
  651.     return width;
  652.     }
  653.  
  654. static int line_size (buf, bufend)
  655.     char *        buf;
  656.     register char *    bufend;
  657.     {
  658.     register int    width;
  659.  
  660.     for (width = 0;  buf < bufend  &&  *buf != '\0';  )
  661.     width += show_char (&buf, width, 0, bufend - buf);
  662.     return width;
  663.     }
  664.  
  665. static void inserttoken (buf, start, tok, curchar)
  666.     char *        buf;
  667.     char *        start; 
  668.     register char *    tok;
  669.     char **        curchar;
  670.     {
  671.     char        copy[BUFSIZ];
  672.     register char *    p;
  673.     register char *    q;
  674.     char *        ew;
  675.  
  676.     (void) strcpy (copy, buf);
  677.  
  678.     for (p = buf, q = copy; p != start; p++, q++)
  679.     *p = *q;
  680.     q += *curchar - start;
  681.     ew = skipoverword (tok);
  682.     while (tok < ew)
  683.     *p++ = *tok++;
  684.     *curchar = p;
  685.     if (*tok)
  686.     {
  687.  
  688.     /*
  689.     ** The token changed to two words.  Split it up and save the
  690.     ** second one for later.
  691.     */
  692.  
  693.     *p++ = *tok;
  694.     *tok++ = '\0';
  695.     while (*tok)
  696.         *p++ = *tok++;
  697.     }
  698.     while ((*p++ = *q++) != '\0')
  699.     ;
  700.     }
  701.  
  702. static int posscmp (a, b)
  703.     char *        a;
  704.     char *        b;
  705.     {
  706.  
  707.     return casecmp (a, b, 0);
  708.     }
  709.  
  710. int casecmp (a, b, canonical)
  711.     char *        a;
  712.     char *        b;
  713.     int            canonical;    /* NZ for canonical string chars */
  714.     {
  715.     register ichar_t *    ap;
  716.     register ichar_t *    bp;
  717.     ichar_t        inta[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  718.     ichar_t        intb[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  719.  
  720.     (void) strtoichar (inta, a, sizeof inta, canonical);
  721.     (void) strtoichar (intb, b, sizeof intb, canonical);
  722.     for (ap = inta, bp = intb;  *ap != 0;  ap++, bp++)
  723.     {
  724.     if (*ap != *bp)
  725.         {
  726.         if (*bp == '\0')
  727.         return hashheader.sortorder[*ap];
  728.         else if (mylower (*ap))
  729.         {
  730.         if (mylower (*bp)  ||  mytoupper (*ap) != *bp)
  731.             return (int) hashheader.sortorder[*ap]
  732.               - (int) hashheader.sortorder[*bp];
  733.         }
  734.         else
  735.         {
  736.         if (myupper (*bp)  ||  mytolower (*ap) != *bp)
  737.             return (int) hashheader.sortorder[*ap]
  738.               - (int) hashheader.sortorder[*bp];
  739.         }
  740.         }
  741.     }
  742.     if (*bp != '\0')
  743.     return -(int) hashheader.sortorder[*bp];
  744.     for (ap = inta, bp = intb;  *ap;  ap++, bp++)
  745.     {
  746.     if (*ap != *bp)
  747.         {
  748.         return (int) hashheader.sortorder[*ap]
  749.           - (int) hashheader.sortorder[*bp];
  750.         }
  751.     }
  752.     return 0;
  753.     }
  754.  
  755. void makepossibilities (word)
  756.     register ichar_t *    word;
  757.     {
  758.     register int    i;
  759.  
  760.     for (i = 0; i < MAXPOSSIBLE; i++)
  761.     possibilities[i][0] = 0;
  762.     pcount = 0;
  763.     maxposslen = 0;
  764.     easypossibilities = 0;
  765.  
  766. #ifndef NO_CAPITALIZATION_SUPPORT
  767.     wrongcapital (word);
  768. #endif
  769.  
  770. /* 
  771.  * according to Pollock and Zamora, CACM April 1984 (V. 27, No. 4),
  772.  * page 363, the correct order for this is:
  773.  * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION
  774.  * thus, it was exactly backwards in the old version. -- PWP
  775.  */
  776.  
  777.     if (pcount < MAXPOSSIBLE)
  778.     missingletter (word);        /* omission */
  779.     if (pcount < MAXPOSSIBLE)
  780.     transposedletter (word);    /* transposition */
  781.     if (pcount < MAXPOSSIBLE)
  782.     extraletter (word);        /* insertion */
  783.     if (pcount < MAXPOSSIBLE)
  784.     wrongletter (word);        /* substitution */
  785.  
  786.     if ((compoundflag != COMPOUND_ANYTIME)  &&  pcount < MAXPOSSIBLE)
  787.     missingspace (word);    /* two words */
  788.  
  789.     easypossibilities = pcount;
  790.     if (easypossibilities == 0  ||  tryhardflag)
  791.     tryveryhard (word);
  792.  
  793.     if ((sortit  ||  (pcount > easypossibilities))  &&  pcount)
  794.     {
  795.     if (easypossibilities > 0  &&  sortit)
  796.         qsort ((char *) possibilities,
  797.           (unsigned) easypossibilities,
  798.           sizeof (possibilities[0]),
  799.           (int (*) P ((const void *, const void *))) posscmp);
  800.     if (pcount > easypossibilities)
  801.         qsort ((char *) &possibilities[easypossibilities][0],
  802.           (unsigned) (pcount - easypossibilities),
  803.           sizeof (possibilities[0]),
  804.           (int (*) P ((const void *, const void *))) posscmp);
  805.     }
  806.     }
  807.  
  808. static int insert (word)
  809.     register ichar_t *    word;
  810.     {
  811.     register int    i;
  812.     register char *    realword;
  813.  
  814.     realword = ichartosstr (word, 0);
  815.     for (i = 0; i < pcount; i++)
  816.     {
  817.     if (strcmp (possibilities[i], realword) == 0)
  818.         return (0);
  819.     }
  820.  
  821.     (void) strcpy (possibilities[pcount++], realword);
  822.     i = strlen (realword);
  823.     if (i > maxposslen)
  824.     maxposslen = i;
  825.     if (pcount >= MAXPOSSIBLE)
  826.     return (-1);
  827.     else
  828.     return (0);
  829.     }
  830.  
  831. #ifndef NO_CAPITALIZATION_SUPPORT
  832. static void wrongcapital (word)
  833.     register ichar_t *    word;
  834.     {
  835.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  836.  
  837.     /*
  838.     ** When the third parameter to "good" is nonzero, it ignores
  839.     ** case.  If the word matches this way, "ins_cap" will recapitalize
  840.     ** it correctly.
  841.     */
  842.     if (good (word, 0, 1, 0, 0))
  843.     {
  844.     (void) icharcpy (newword, word);
  845.     upcase (newword);
  846.     (void) ins_cap (newword, word);
  847.     }
  848.     }
  849. #endif
  850.  
  851. static void wrongletter (word)
  852.     register ichar_t *    word;
  853.     {
  854.     register int    i;
  855.     register int    j;
  856.     register int    n;
  857.     ichar_t        savechar;
  858.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  859.  
  860.     n = icharlen (word);
  861.     (void) icharcpy (newword, word);
  862. #ifndef NO_CAPITALIZATION_SUPPORT
  863.     upcase (newword);
  864. #endif
  865.  
  866.     for (i = 0; i < n; i++)
  867.     {
  868.     savechar = newword[i];
  869.     for (j=0; j < Trynum; ++j)
  870.         {
  871.         if (Try[j] == savechar)
  872.         continue;
  873.         else if (isboundarych (Try[j])  &&  (i == 0  ||  i == n - 1))
  874.         continue;
  875.         newword[i] = Try[j];
  876.         if (good (newword, 0, 1, 0, 0))
  877.         {
  878.         if (ins_cap (newword, word) < 0)
  879.             return;
  880.         }
  881.         }
  882.     newword[i] = savechar;
  883.     }
  884.     }
  885.  
  886. static void extraletter (word)
  887.     register ichar_t *    word;
  888.     {
  889.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  890.     register ichar_t *    p;
  891.     register ichar_t *    r;
  892.  
  893.     if (icharlen (word) < 2)
  894.     return;
  895.  
  896.     (void) icharcpy (newword, word + 1);
  897.     for (p = word, r = newword;  *p != 0;  )
  898.     {
  899.     if (good (newword, 0, 1, 0, 0))
  900.         {
  901.         if (ins_cap (newword, word) < 0)
  902.         return;
  903.         }
  904.     *r++ = *p++;
  905.     }
  906.     }
  907.  
  908. static void missingletter (word)
  909.     ichar_t *        word;
  910.     {
  911.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
  912.     register ichar_t *    p;
  913.     register ichar_t *    r;
  914.     register int    i;
  915.  
  916.     (void) icharcpy (newword + 1, word);
  917.     for (p = word, r = newword;  *p != 0;  )
  918.     {
  919.     for (i = 0;  i < Trynum;  i++)
  920.         {
  921.         if (isboundarych (Try[i])  &&  r == newword)
  922.         continue;
  923.         *r = Try[i];
  924.         if (good (newword, 0, 1, 0, 0))
  925.         {
  926.         if (ins_cap (newword, word) < 0)
  927.             return;
  928.         }
  929.         }
  930.     *r++ = *p++;
  931.     }
  932.     for (i = 0;  i < Trynum;  i++)
  933.     {
  934.     if (isboundarych (Try[i]))
  935.         continue;
  936.     *r = Try[i];
  937.     if (good (newword, 0, 1, 0, 0))
  938.         {
  939.         if (ins_cap (newword, word) < 0)
  940.         return;
  941.         }
  942.     }
  943.     }
  944.  
  945. static void missingspace (word)
  946.     ichar_t *        word;
  947.     {
  948.     ichar_t        firsthalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  949.     int            firstno;    /* Index into first */
  950.     ichar_t *        firstp;        /* Ptr into current firsthalf word */
  951.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
  952.     int            nfirsthalf;    /* No. words saved in 1st half */
  953.     int            nsecondhalf;    /* No. words saved in 2nd half */
  954.     register ichar_t *    p;
  955.     ichar_t        secondhalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  956.     int            secondno;    /* Index into second */
  957.  
  958.     /*
  959.     ** We don't do words of length less than 3;  this keeps us from
  960.     ** splitting all two-letter words into two single letters.  We
  961.     ** also don't do maximum-length words, since adding the space
  962.     ** would exceed the size of the "possibilities" array.
  963.     */
  964.     nfirsthalf = icharlen (word);
  965.     if (nfirsthalf < 3  ||  nfirsthalf >= INPUTWORDLEN + MAXAFFIXLEN - 1)
  966.     return;
  967.     (void) icharcpy (newword + 1, word);
  968.     for (p = newword + 1;  p[1] != '\0';  p++)
  969.     {
  970.     p[-1] = *p;
  971.     *p = '\0';
  972.     if (good (newword, 0, 1, 0, 0))
  973.         {
  974.         /*
  975.          * Save_cap must be called before good() is called on the
  976.          * second half, because it uses state left around by
  977.          * good().  This is unfortunate because it wastes a bit of
  978.          * time, but I don't think it's a significant performance
  979.          * problem.
  980.          */
  981.         nfirsthalf = save_cap (newword, word, firsthalf);
  982.         if (good (p + 1, 0, 1, 0, 0))
  983.         {
  984.         nsecondhalf = save_cap (p + 1, p + 1, secondhalf);
  985.         for (firstno = 0;  firstno < nfirsthalf;  firstno++)
  986.             {
  987.             firstp = &firsthalf[firstno][p - newword];
  988.             for (secondno = 0;  secondno < nsecondhalf;  secondno++)
  989.             {
  990.             *firstp = ' ';
  991.             (void) icharcpy (firstp + 1, secondhalf[secondno]);
  992.             if (insert (firsthalf[firstno]) < 0)
  993.                 return;
  994.             *firstp = '-';
  995.             if (insert (firsthalf[firstno]) < 0)
  996.                 return;
  997.             }
  998.             }
  999.         }
  1000.         }
  1001.     }
  1002.     }
  1003.  
  1004. int compoundgood (word, pfxopts)
  1005.     ichar_t *        word;
  1006.     int            pfxopts;    /* Options to apply to prefixes */
  1007.     {
  1008.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  1009.     register ichar_t *    p;
  1010.     register ichar_t    savech;
  1011.     long        secondcap;    /* Capitalization of 2nd half */
  1012.  
  1013.     /*
  1014.     ** If compoundflag is COMPOUND_NEVER, compound words are never ok.
  1015.     */
  1016.     if (compoundflag == COMPOUND_NEVER)
  1017.     return 0;
  1018.     /*
  1019.     ** Test for a possible compound word (for languages like German that
  1020.     ** form lots of compounds).
  1021.     **
  1022.     ** This is similar to missingspace, except we quit on the first hit,
  1023.     ** and we won't allow either member of the compound to be a single
  1024.     ** letter.
  1025.     **
  1026.     ** We don't do words of length less than 2 * compoundmin, since
  1027.     ** both halves must at least compoundmin letters.
  1028.     */
  1029.     if (icharlen (word) < 2 * hashheader.compoundmin)
  1030.     return 0;
  1031.     (void) icharcpy (newword, word);
  1032.     p = newword + hashheader.compoundmin;
  1033.     for (  ;  p[hashheader.compoundmin - 1] != 0;  p++)
  1034.     {
  1035.     savech = *p;
  1036.     *p = 0;
  1037.     if (good (newword, 0, 0, pfxopts, FF_COMPOUNDONLY))
  1038.         {
  1039.         *p = savech;
  1040.         if (good (p, 0, 1, FF_COMPOUNDONLY, 0)
  1041.           ||  compoundgood (p, FF_COMPOUNDONLY))
  1042.         {
  1043.         secondcap = whatcap (p);
  1044.         switch (whatcap (newword))
  1045.             {
  1046.             case ANYCASE:
  1047.             case CAPITALIZED:
  1048.             case FOLLOWCASE:    /* Followcase can have l.c. suffix */
  1049.             return secondcap == ANYCASE;
  1050.             case ALLCAPS:
  1051.             return secondcap == ALLCAPS;
  1052.             }
  1053.         }
  1054.         }
  1055.     else
  1056.         *p = savech;
  1057.     }
  1058.     return 0;
  1059.     }
  1060.  
  1061. static void transposedletter (word)
  1062.     register ichar_t *    word;
  1063.     {
  1064.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  1065.     register ichar_t *    p;
  1066.     register ichar_t    temp;
  1067.  
  1068.     (void) icharcpy (newword, word);
  1069.     for (p = newword;  p[1] != 0;  p++)
  1070.     {
  1071.     temp = *p;
  1072.     *p = p[1];
  1073.     p[1] = temp;
  1074.     if (good (newword, 0, 1, 0, 0))
  1075.         {
  1076.         if (ins_cap (newword, word) < 0)
  1077.         return;
  1078.         }
  1079.     temp = *p;
  1080.     *p = p[1];
  1081.     p[1] = temp;
  1082.     }
  1083.     }
  1084.  
  1085. static void tryveryhard (word)
  1086.     ichar_t *        word;
  1087.     {
  1088.     (void) good (word, 1, 0, 0, 0);
  1089.     }
  1090.  
  1091. /* Insert one or more correctly capitalized versions of word */
  1092. static int ins_cap (word, pattern)
  1093.     ichar_t *        word;
  1094.     ichar_t *        pattern;
  1095.     {
  1096.     int            i;        /* Index into savearea */
  1097.     int            nsaved;        /* No. of words saved */
  1098.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1099.  
  1100.     nsaved = save_cap (word, pattern, savearea);
  1101.     for (i = 0;  i < nsaved;  i++)
  1102.     {
  1103.     if (insert (savearea[i]) < 0)
  1104.         return -1;
  1105.     }
  1106.     return 0;
  1107.     }
  1108.  
  1109. /* Save one or more correctly capitalized versions of word */
  1110. static int save_cap (word, pattern, savearea)
  1111.     ichar_t *        word;        /* Word to save */
  1112.     ichar_t *        pattern;    /* Prototype capitalization pattern */
  1113.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1114.                     /* Room to save words */
  1115.     {
  1116.     int            hitno;        /* Index into hits array */
  1117.     int            nsaved;        /* Number of words saved */
  1118.     int            preadd;        /* No. chars added to front of root */
  1119.     int            prestrip;    /* No. chars stripped from front */
  1120.     int            sufadd;        /* No. chars added to back of root */
  1121.     int            sufstrip;    /* No. chars stripped from back */
  1122.  
  1123.     if (*word == 0)
  1124.     return 0;
  1125.  
  1126.     for (hitno = numhits, nsaved = 0;  --hitno >= 0  &&  nsaved < MAX_CAPS;  )
  1127.     {
  1128.     if (hits[hitno].prefix)
  1129.         {
  1130.         prestrip = hits[hitno].prefix->stripl;
  1131.         preadd = hits[hitno].prefix->affl;
  1132.         }
  1133.     else
  1134.         prestrip = preadd = 0;
  1135.     if (hits[hitno].suffix)
  1136.         {
  1137.         sufstrip = hits[hitno].suffix->stripl;
  1138.         sufadd = hits[hitno].suffix->affl;
  1139.         }
  1140.     else
  1141.         sufadd = sufstrip = 0;
  1142.     save_root_cap (word, pattern, prestrip, preadd,
  1143.         sufstrip, sufadd,
  1144.         hits[hitno].dictent, hits[hitno].prefix, hits[hitno].suffix,
  1145.         savearea, &nsaved);
  1146.     }
  1147.     return nsaved;
  1148.     }
  1149.  
  1150. int ins_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1151.   firstdent, pfxent, sufent)
  1152.     register ichar_t *    word;
  1153.     register ichar_t *    pattern;
  1154.     int            prestrip;
  1155.     int            preadd;
  1156.     int            sufstrip;
  1157.     int            sufadd;
  1158.     struct dent *    firstdent;
  1159.     struct flagent *    pfxent;
  1160.     struct flagent *    sufent;
  1161.     {
  1162.     int            i;        /* Index into savearea */
  1163.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1164.     int            nsaved;        /* Number of words saved */
  1165.  
  1166.     nsaved = 0;
  1167.     save_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1168.       firstdent, pfxent, sufent, savearea, &nsaved);
  1169.     for (i = 0;  i < nsaved;  i++)
  1170.     {
  1171.     if (insert (savearea[i]) < 0)
  1172.         return -1;
  1173.     }
  1174.     return 0;
  1175.     }
  1176.  
  1177. /* ARGSUSED */
  1178. static void save_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1179.   firstdent, pfxent, sufent, savearea, nsaved)
  1180.     register ichar_t *    word;        /* Word to be saved */
  1181.     register ichar_t *    pattern;    /* Capitalization pattern */
  1182.     int            prestrip;    /* No. chars stripped from front */
  1183.     int            preadd;        /* No. chars added to front of root */
  1184.     int            sufstrip;    /* No. chars stripped from back */
  1185.     int            sufadd;        /* No. chars added to back of root */
  1186.     struct dent *    firstdent;    /* First dent for root */
  1187.     struct flagent *    pfxent;        /* Pfx-flag entry for word */
  1188.     struct flagent *    sufent;        /* Sfx-flag entry for word */
  1189.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1190.                     /* Room to save words */
  1191.     int *        nsaved;        /* Number saved so far (updated) */
  1192.     {
  1193. #ifndef NO_CAPITALIZATION_SUPPORT
  1194.     register struct dent * dent;
  1195. #endif /* NO_CAPITALIZATION_SUPPORT */
  1196.     int            firstisupper;
  1197.     ichar_t        newword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  1198. #ifndef NO_CAPITALIZATION_SUPPORT
  1199.     register ichar_t *    p;
  1200.     int            len;
  1201.     int            i;
  1202.     int            limit;
  1203. #endif /* NO_CAPITALIZATION_SUPPORT */
  1204.  
  1205.     if (*nsaved >= MAX_CAPS)
  1206.     return;
  1207.     (void) icharcpy (newword, word);
  1208.     firstisupper = myupper (pattern[0]);
  1209. #ifdef NO_CAPITALIZATION_SUPPORT
  1210.     /*
  1211.     ** Apply the old, simple-minded capitalization rules.
  1212.     */
  1213.     if (firstisupper)
  1214.     {
  1215.     if (myupper (pattern[1]))
  1216.         upcase (newword);
  1217.     else
  1218.         {
  1219.         lowcase (newword);
  1220.         newword[0] = mytoupper (newword[0]);
  1221.         }
  1222.     }
  1223.     else
  1224.     lowcase (newword);
  1225.     (void) icharcpy (savearea[*nsaved], newword);
  1226.     (*nsaved)++;
  1227.     return;
  1228. #else /* NO_CAPITALIZATION_SUPPORT */
  1229. #define flagsareok(dent)    \
  1230.     ((pfxent == NULL \
  1231.     ||  TSTMASKBIT (dent->mask, pfxent->flagbit)) \
  1232.       &&  (sufent == NULL \
  1233.     ||  TSTMASKBIT (dent->mask, sufent->flagbit)))
  1234.  
  1235.     dent = firstdent;
  1236.     if ((dent->flagfield & (CAPTYPEMASK | MOREVARIANTS)) == ALLCAPS)
  1237.     {
  1238.     upcase (newword);    /* Uppercase required */
  1239.     (void) icharcpy (savearea[*nsaved], newword);
  1240.     (*nsaved)++;
  1241.     return;
  1242.     }
  1243.     for (p = pattern;  *p;  p++)
  1244.     {
  1245.     if (mylower (*p))
  1246.         break;
  1247.     }
  1248.     if (*p == 0)
  1249.     {
  1250.     upcase (newword);    /* Pattern was all caps */
  1251.     (void) icharcpy (savearea[*nsaved], newword);
  1252.     (*nsaved)++;
  1253.     return;
  1254.     }
  1255.     for (p = pattern + 1;  *p;  p++)
  1256.     {
  1257.     if (myupper (*p))
  1258.         break;
  1259.     }
  1260.     if (*p == 0)
  1261.     {
  1262.     /*
  1263.     ** The pattern was all-lower or capitalized.  If that's
  1264.     ** legal, insert only that version.
  1265.     */
  1266.     if (firstisupper)
  1267.         {
  1268.         if (captype (dent->flagfield) == CAPITALIZED
  1269.           ||  captype (dent->flagfield) == ANYCASE)
  1270.         {
  1271.         lowcase (newword);
  1272.         newword[0] = mytoupper (newword[0]);
  1273.         (void) icharcpy (savearea[*nsaved], newword);
  1274.         (*nsaved)++;
  1275.         return;
  1276.         }
  1277.         }
  1278.     else
  1279.         {
  1280.         if (captype (dent->flagfield) == ANYCASE)
  1281.         {
  1282.         lowcase (newword);
  1283.         (void) icharcpy (savearea[*nsaved], newword);
  1284.         (*nsaved)++;
  1285.         return;
  1286.         }
  1287.         }
  1288.     while (dent->flagfield & MOREVARIANTS)
  1289.         {
  1290.         dent = dent->next;
  1291.         if (captype (dent->flagfield) == FOLLOWCASE
  1292.           ||  !flagsareok (dent))
  1293.         continue;
  1294.         if (firstisupper)
  1295.         {
  1296.         if (captype (dent->flagfield) == CAPITALIZED)
  1297.             {
  1298.             lowcase (newword);
  1299.             newword[0] = mytoupper (newword[0]);
  1300.             (void) icharcpy (savearea[*nsaved], newword);
  1301.             (*nsaved)++;
  1302.             return;
  1303.             }
  1304.         }
  1305.         else
  1306.         {
  1307.         if (captype (dent->flagfield) == ANYCASE)
  1308.             {
  1309.             lowcase (newword);
  1310.             (void) icharcpy (savearea[*nsaved], newword);
  1311.             (*nsaved)++;
  1312.             return;
  1313.             }
  1314.         }
  1315.         }
  1316.     }
  1317.     /*
  1318.     ** Either the sample had complex capitalization, or the simple
  1319.     ** capitalizations (all-lower or capitalized) are illegal.
  1320.     ** Insert all legal capitalizations, including those that are
  1321.     ** all-lower or capitalized.  If the prototype is capitalized,
  1322.     ** capitalized all-lower samples.  Watch out for affixes.
  1323.     */
  1324.     dent = firstdent;
  1325.     p = strtosichar (dent->word, 1);
  1326.     len = icharlen (p);
  1327.     if (dent->flagfield & MOREVARIANTS)
  1328.     dent = dent->next;    /* Skip place-holder entry */
  1329.     for (  ;  ;  )
  1330.     {
  1331.     if (flagsareok (dent))
  1332.         {
  1333.         if (captype (dent->flagfield) != FOLLOWCASE)
  1334.         {
  1335.         lowcase (newword);
  1336.         if (firstisupper  ||  captype (dent->flagfield) == CAPITALIZED)
  1337.             newword[0] = mytoupper (newword[0]);
  1338.         (void) icharcpy (savearea[*nsaved], newword);
  1339.         (*nsaved)++;
  1340.         if (*nsaved >= MAX_CAPS)
  1341.             return;
  1342.         }
  1343.         else
  1344.         {
  1345.         /* Followcase is the tough one. */
  1346.         p = strtosichar (dent->word, 1);
  1347.         (void) bcopy ((char *) (p + prestrip),
  1348.           (char *) (newword + preadd),
  1349.           (len - prestrip - sufstrip) * sizeof (ichar_t));
  1350.         if (myupper (p[prestrip]))
  1351.             {
  1352.             for (i = 0;  i < preadd;  i++)
  1353.             newword[i] = mytoupper (newword[i]);
  1354.             }
  1355.         else
  1356.             {
  1357.             for (i = 0;  i < preadd;  i++)
  1358.             newword[i] = mytolower (newword[i]);
  1359.             }
  1360.         limit = len + preadd + sufadd - prestrip - sufstrip;
  1361.         i = len + preadd - prestrip - sufstrip;
  1362.         p += len - sufstrip - 1;
  1363.         if (myupper (*p))
  1364.             {
  1365.             for (p = newword + i;  i < limit;  i++, p++)
  1366.             *p = mytoupper (*p);
  1367.             }
  1368.         else
  1369.             {
  1370.             for (p = newword + i;  i < limit;  i++, p++)
  1371.               *p = mytolower (*p);
  1372.             }
  1373.         (void) icharcpy (savearea[*nsaved], newword);
  1374.         (*nsaved)++;
  1375.         if (*nsaved >= MAX_CAPS)
  1376.             return;
  1377.         }
  1378.         }
  1379.     if ((dent->flagfield & MOREVARIANTS) == 0)
  1380.         break;        /* End of the line */
  1381.     dent = dent->next;
  1382.     }
  1383.     return;
  1384. #endif /* NO_CAPITALIZATION_SUPPORT */
  1385.     }
  1386.  
  1387. static char * getline (s)
  1388.     register char *    s;
  1389.     {
  1390.     register char *    p;
  1391.     register int    c;
  1392.  
  1393.     p = s;
  1394.  
  1395.     for (  ;  ;  )
  1396.     {
  1397.     (void) fflush (stdout);
  1398.     c = (GETKEYSTROKE () & NOPARITY);
  1399.     if (c == '\\')
  1400.         {
  1401.         (void) putchar ('\\');
  1402.         (void) fflush (stdout);
  1403.         c = (GETKEYSTROKE () & NOPARITY);
  1404.         backup ();
  1405.         (void) putchar (c);
  1406.         *p++ = (char) c;
  1407.         }
  1408.     else if (c == ('G' & 037))
  1409.         return (NULL);
  1410.     else if (c == '\n' || c == '\r')
  1411.         {
  1412.         *p = 0;
  1413.         return (s);
  1414.         }
  1415.     else if (c == uerasechar)
  1416.         {
  1417.         if (p != s)
  1418.         {
  1419.         p--;
  1420.         backup ();
  1421.         (void) putchar (' ');
  1422.         backup ();
  1423.         }
  1424.         }
  1425.     else if (c == ukillchar)
  1426.         {
  1427.         while (p != s)
  1428.         {
  1429.         p--;
  1430.         backup ();
  1431.         (void) putchar (' ');
  1432.         backup ();
  1433.         }
  1434.         }
  1435.     else
  1436.         {
  1437.         *p++ = (char) c;
  1438.         (void) putchar (c);
  1439.         }
  1440.     }
  1441.     }
  1442.  
  1443. void askmode ()
  1444.     {
  1445.     int            bufsize;    /* Length of contextbufs[0] */
  1446.     int            ch;        /* Next character read from input */
  1447.     register char *    cp1;
  1448.     register char *    cp2;
  1449.     ichar_t *        itok;        /* Ichar version of current word */
  1450.     int            hadnl;        /* NZ if \n was at end of line */
  1451.  
  1452.     if (fflag)
  1453.     {
  1454.     if (freopen (askfilename, "w", stdout) == NULL)
  1455.         {
  1456.         (void) fprintf (stderr, CANT_CREATE, askfilename);
  1457.         exit (1);
  1458.         }
  1459.     }
  1460.  
  1461.     (void) printf ("%s\n", Version_ID[0]);
  1462.  
  1463.     contextoffset = 0;
  1464.     while (1)
  1465.     {
  1466.     (void) fflush (stdout);
  1467.     /*
  1468.      * Only read in enough characters to fill half this buffer so that any
  1469.      * corrections we make are not likely to cause an overflow.
  1470.      */
  1471.     if (contextoffset == 0)
  1472.         {
  1473.         if (xgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
  1474.           == NULL)
  1475.         break;
  1476.         }
  1477.     else
  1478.         {
  1479.         if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
  1480.           == NULL)
  1481.         break;
  1482.         }
  1483.     /*
  1484.      * If we didn't read to end-of-line, we may have ended the
  1485.      * buffer in the middle of a word.  So keep reading until we
  1486.      * see some sort of character that can't possibly be part of a
  1487.      * word. (or until the buffer is full, which fortunately isn't
  1488.      * all that likely).
  1489.      */
  1490.     bufsize = strlen (contextbufs[0]);
  1491.     if (contextbufs[0][bufsize - 1] == '\n')
  1492.         {
  1493.         hadnl = 1;
  1494.         contextbufs[0][--bufsize] = '\0';
  1495.         }
  1496.     else
  1497.         hadnl = 0;
  1498.     if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
  1499.         {
  1500.         ch = (unsigned char) contextbufs[0][bufsize - 1];
  1501.         while (bufsize < sizeof contextbufs[0] - 1
  1502.           &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
  1503.           ||  isstringstart (ch)))
  1504.         {
  1505.         ch = getc (stdin);
  1506.         if (ch == EOF)
  1507.             break;
  1508.         contextbufs[0][bufsize++] = (char) ch;
  1509.         contextbufs[0][bufsize] = '\0';
  1510.         }
  1511.         }
  1512.     /*
  1513.     ** *line is like `i', @line is like `a', &line is like 'u'
  1514.     ** `#' is like `Q' (writes personal dictionary)
  1515.     ** `+' sets tflag, `-' clears tflag
  1516.     ** `!' sets terse mode, `%' clears terse
  1517.     ** `~' followed by a filename sets parameters according to file name
  1518.     ** `^' causes rest of line to be checked after stripping 1st char
  1519.     */
  1520.     if (contextoffset != 0)
  1521.         checkline (stdout);
  1522.     else
  1523.         {
  1524.         if (contextbufs[0][0] == '*'  ||  contextbufs[0][0] == '@')
  1525.         treeinsert(ichartosstr (strtosichar (contextbufs[0] + 1, 0), 1),
  1526.           ICHARTOSSTR_SIZE,
  1527.           contextbufs[0][0] == '*');
  1528.         else if (contextbufs[0][0] == '&')
  1529.         {
  1530.         itok = strtosichar (contextbufs[0] + 1, 0);
  1531.         lowcase (itok);
  1532.         treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
  1533.         }
  1534.         else if (contextbufs[0][0] == '#')
  1535.         {
  1536.         treeoutput ();
  1537.         math_mode = 0;
  1538.         LaTeX_Mode = 'P';
  1539.         }
  1540.         else if (contextbufs[0][0] == '!')
  1541.         terse = 1;
  1542.         else if (contextbufs[0][0] == '%')
  1543.         terse = 0;
  1544.         else if (contextbufs[0][0] == '-')
  1545.         {
  1546.         math_mode = 0;
  1547.         LaTeX_Mode = 'P';
  1548.         tflag = 0;
  1549.         }
  1550.         else if (contextbufs[0][0] == '+')
  1551.         {
  1552.         math_mode = 0;
  1553.         LaTeX_Mode = 'P';
  1554.         tflag = strcmp (&contextbufs[0][1], "nroff") != 0
  1555.           &&  strcmp (&contextbufs[0][1], "troff") != 0;
  1556.         }
  1557.         else if (contextbufs[0][0] == '~')
  1558.         {
  1559.         defdupchar = findfiletype (&contextbufs[0][1], 1, (int *) NULL);
  1560.         if (defdupchar < 0)
  1561.             defdupchar = 0;
  1562.         }
  1563.         else
  1564.         {
  1565.         if (contextbufs[0][0] == '^')
  1566.             {
  1567.             /* Strip off leading uparrow */
  1568.             for (cp1 = contextbufs[0], cp2 = contextbufs[0] + 1;
  1569.               (*cp1++ = *cp2++) != '\0';
  1570.               )
  1571.             ;
  1572.             contextoffset++;
  1573.             bufsize--;
  1574.             }
  1575.         checkline (stdout);
  1576.         }
  1577.         }
  1578.     if (hadnl)
  1579.         contextoffset = 0;
  1580.     else
  1581.         contextoffset += bufsize;
  1582. #ifndef USG
  1583.     if (sflag)
  1584.         {
  1585.         stop ();
  1586.         if (fflag)
  1587.         {
  1588.         rewind (stdout);
  1589.         (void) creat (askfilename, 0666);
  1590.         }
  1591.         }
  1592. #endif
  1593.     }
  1594.     }
  1595.  
  1596. /* Copy/ignore "cnt" number of characters pointed to by *cc. */
  1597. void copyout (cc, cnt)
  1598.     register char **    cc;
  1599.     register int    cnt;
  1600.     {
  1601.  
  1602.     while (--cnt >= 0)
  1603.     {
  1604.     if (**cc == '\0')
  1605.         break;
  1606.     if (!aflag && !lflag)
  1607.         (void) putc (**cc, outfile);
  1608.     (*cc)++;
  1609.     }
  1610.     }
  1611.  
  1612. static void lookharder (string)
  1613.     char *        string;
  1614.     {
  1615.     char        cmd[150];
  1616.     char        grepstr[100];
  1617.     register char *    g;
  1618.     register char *    s;
  1619. #ifndef REGEX_LOOKUP
  1620.     register int    wild = 0;
  1621. #ifdef LOOK
  1622.     static int        look = -1;
  1623. #endif /* LOOK */
  1624. #endif /* REGEX_LOOKUP */
  1625.  
  1626.     g = grepstr;
  1627.     for (s = string; *s != '\0'; s++)
  1628.     {
  1629.     if (*s == '*')
  1630.         {
  1631. #ifndef REGEX_LOOKUP
  1632.         wild++;
  1633. #endif /* REGEX_LOOKUP */
  1634.         *g++ = '.';
  1635.         *g++ = '*';
  1636.         }
  1637.     else
  1638.         *g++ = *s;
  1639.     }
  1640.     *g = '\0';
  1641.     if (grepstr[0])
  1642.     {
  1643. #ifdef REGEX_LOOKUP
  1644.     regex_dict_lookup (cmd, grepstr);
  1645. #else /* REGEX_LOOKUP */
  1646. #ifdef LOOK
  1647.     /* now supports automatic use of look - gms */
  1648.     if (!wild && look)
  1649.         {
  1650.         /* no wild and look(1) is possibly available */
  1651.         (void) sprintf (cmd, "%s %s %s", LOOK, grepstr, WORDS);
  1652.         if (shellescape (cmd))
  1653.         return;
  1654.         else
  1655.         look = 0;
  1656.         }
  1657. #endif /* LOOK */
  1658.     /* string has wild card chars or look not avail */
  1659.     if (!wild)
  1660.         (void) strcat (grepstr, ".*");    /* work like look */
  1661.     (void) sprintf (cmd, "%s ^%s$ %s", EGREPCMD, grepstr, WORDS);
  1662.     (void) shellescape (cmd);
  1663. #endif /* REGEX_LOOKUP */
  1664.     }
  1665.     }
  1666.  
  1667. #ifdef REGEX_LOOKUP
  1668. static void regex_dict_lookup (cmd, grepstr)
  1669.     char *        cmd;
  1670.     char *        grepstr;
  1671.     {
  1672.     char *        rval;
  1673.     int            whence = 0;
  1674.     int            quitlookup = 0;
  1675.     int            count = 0;
  1676.     int            ch;
  1677.  
  1678.     (void) sprintf (cmd, "^%s$", grepstr);
  1679.     while (!quitlookup  &&  (rval = do_regex_lookup (cmd, whence)) != NULL)
  1680.     {
  1681.     whence = 1;
  1682.         (void) printf ("%s\r\n", rval);;
  1683.     if ((++count % (li - 1)) == 0)
  1684.         {
  1685.         inverse ();
  1686.         (void) printf (CORR_C_MORE_PROMPT);
  1687.         normal ();
  1688.         (void) fflush (stdout);
  1689.         if ((ch = GETKEYSTROKE ()) == 'q'
  1690.           ||  ch == 'Q'  ||  ch == 'x'  ||  ch == 'X' )
  1691.              quitlookup = 1;
  1692.         /*
  1693.          * The following line should blank out the -- more -- even on
  1694.          * magic-cookie terminals.
  1695.          */
  1696.         (void) printf (CORR_C_BLANK_MORE);
  1697.         (void) fflush (stdout);
  1698.         }
  1699.     }
  1700.     if ( rval == NULL )
  1701.     {
  1702.     inverse ();
  1703.     (void) printf (CORR_C_END_LOOK);
  1704.     normal ();
  1705.     (void) fflush (stdout);
  1706.     (void) GETKEYSTROKE ();    
  1707.     }
  1708.     }
  1709.  
  1710. #endif /* REGEX_LOOKUP */
  1711.